namespace ScrumSprintMonitor.Modules.WorkItemMonitor.ViewModels
{
	#region #using Directives

	using System.Collections.Generic;
	using System.Linq;
	using System.Threading;

	using Infrastructure;

	using Microsoft.Practices.ServiceLocation;

	using UI.Wpf.PostSharp;
	using UI.Wpf.Properties;
	using UI.Wpf.ViewModels;

	#endregion

	[GuardDisposed]
	public class SprintHoursChartViewModel : SprintChartViewModelBase<IWorkItemHoursPieChartData>,
	                                         ISprintHoursChartViewModel
	{
		static SprintHoursChartViewModel()
		{
			var dependsOn = Register<SprintHoursChartViewModel>();

			dependsOn.Call(me =>
			               {
			               	if (me.PieChartData.Any())
			               	{
			               		me.GetSprintHoursPieChartData(0).Value = me.ReadyForTestWork_TotalHours;
			               	}
			               }).OnChanged(me => me.ReadyForTestWork_TotalHours);
			dependsOn.Call(me =>
			               {
			               	if (me.PieChartData.Any())
			               	{
			               		me.GetSprintHoursPieChartData(1).Value = me.ActiveWork_TotalHoursDone;
			               	}
			               }).OnChanged(me => me.ActiveWork_TotalHoursDone);
			dependsOn.Call(me =>
			               {
			               	if (me.PieChartData.Any())
			               	{
			               		me.GetSprintHoursPieChartData(2).Value = me.ActiveWork_TotalHoursRemaining;
			               	}
			               }).OnChanged(me => me.ActiveWork_TotalHoursRemaining);
			dependsOn.Call(me =>
			               {
			               	if (me.PieChartData.Any())
			               	{
			               		me.GetSprintHoursPieChartData(3).Value = me.NotDoneWork_TotalHours;
			               	}
			               }).OnChanged(me => me.NotDoneWork_TotalHours);
		}

		public SprintHoursChartViewModel(IServiceLocator serviceLocator)
			: base(serviceLocator)
		{
		}

		#region Private Fields

		#endregion

		#region Private Methods

		protected override void OnSprintDataInitialized()
		{
			if (PieChartData.Count == 4)
			{
				return;
			}

			PieChartData.Clear();

			PieChartData.Add(new WorkItemHoursPieChartData
			                 	{
			                 		Value = ReadyForTestWork_TotalHours,
			                 		StateName = Resources.WorkItemState_ReadyForTest
			                 	});
			PieChartData.Add(new WorkItemHoursPieChartData
			                 	{
			                 		Value = ActiveWork_TotalHoursDone,
			                 		StateName = Resources.WorkItemState_Active_Done
			                 	});
			PieChartData.Add(new WorkItemHoursPieChartData
			                 	{
			                 		Value = ActiveWork_TotalHoursRemaining,
			                 		StateName = Resources.WorkItemState_Active_Remaining
			                 	});
			PieChartData.Add(new WorkItemHoursPieChartData
			                 	{
			                 		Value = NotDoneWork_TotalHours,
			                 		StateName = Resources.WorkItemState_NotDone
			                 	});
		}

		protected override void OnCalculateWorkItemData(IList<IWorkItemAdapter> workItems)
		{
			double readyForTestWork = 0.0, activeWorkDone = 0.0, activeWorkRemaining = 0.0, notDoneWork = 0.0;

			var sprintBacklogItems = workItems.Where(x => x.Type == WorkItemType.SprintBacklogItem);
			Parallel.Invoke(() => readyForTestWork = sprintBacklogItems.Where(x => x.State == WorkItemState.Done).Sum(x => x.DoneWork),
			                () => activeWorkDone = sprintBacklogItems.Where(x => x.State == WorkItemState.InProgress).Sum(x => x.DoneWork),
			                () =>
			                activeWorkRemaining =
			                sprintBacklogItems.Where(x => x.State == WorkItemState.InProgress && !double.IsNaN(x.RemainingWork)).Sum(x => x.RemainingWork),
			                () =>
			                notDoneWork =
			                sprintBacklogItems.Where(x => x.State == WorkItemState.NotDone && !double.IsNaN(x.RemainingWork)).Sum(x => x.RemainingWork));

			ReadyForTestWork_TotalHours = readyForTestWork;
			ActiveWork_TotalHoursDone = activeWorkDone;
			ActiveWork_TotalHoursRemaining = activeWorkRemaining;
			NotDoneWork_TotalHours = notDoneWork;

			UpdateActualValueCanBeHigher(workItems);
		}

		private void UpdateActualValueCanBeHigher(IEnumerable<IWorkItemAdapter> workItems)
		{
			if (PieChartData.Count == 0)
			{
				return;
			}

			GetSprintHoursPieChartData(2).ActualValueCanBeHigher =
				workItems.Where(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.InProgress).Any(x => double.IsNaN(x.RemainingWork));
			GetSprintHoursPieChartData(3).ActualValueCanBeHigher =
				workItems.Where(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.NotDone).Any(x => double.IsNaN(x.RemainingWork));
		}

		protected IWorkItemHoursPieChartData GetSprintHoursPieChartData(int index)
		{
			return PieChartData[index];
		}

		#endregion

		#region Implementation of ISprintChartViewModel

		public override string Caption
		{
			get { return Resources.ChartCaption_Work; }
		}

		#endregion

		#region ISprintHoursChartViewModel Members

		[Notify]
		public double ActiveWork_TotalHoursDone { get; private set; }

		[Notify]
		public double ActiveWork_TotalHoursRemaining { get; private set; }

		[Notify]
		public double ReadyForTestWork_TotalHours { get; private set; }

		[Notify]
		public double NotDoneWork_TotalHours { get; private set; }

		#endregion
	}
}